package raytracer.misc;

import java.util.ArrayList;

import raytracer.objects.BoundingBox;
import raytracer.objects.ThreeDObject;

public class KDTree {

	public KDNode root;

	public static KDNode createKDTree(ArrayList<ThreeDObject> objectList, int depth) {

		// get correct axis
		int splitAxis = depth % 3;
		int objectPosition = 0;
		double splitPoint = 0;
		ArrayList<ThreeDObject> ch1 = new ArrayList<ThreeDObject>();
		ArrayList<ThreeDObject> ch2 = new ArrayList<ThreeDObject>();

		// reached maximum tree criteria, create leaf
		if (objectList.size() <= AppWideConstants.MAX_OBJECTS_PER_NODE || depth >= AppWideConstants.MAX_KDTREE_DEPTH) {
			// System.out.println("created Leaf with Objects: " +
			// objectList.size());
			return new KDNode(objectList, null, null, splitAxis, 0, new BoundingBox(objectList));
		} else {
			// where to split the axis
			splitPoint = spaceMean(objectList, splitAxis);
			for (ThreeDObject obj : objectList) {
				// determine if object is on, left or right of that point on the
				// axis and set object lists accordingly
				objectPosition = getObjectPosition(obj, splitPoint, splitAxis);
				switch (objectPosition) {
				case 0:
					ch1.add(obj);
					ch2.add(obj);
					break;

				case 1:
					ch1.add(obj);
					break;

				case 2:
					ch2.add(obj);
					break;

				default:
					break;
				}
			}

		}
		return new KDNode(null, createKDTree(ch1, depth + 1), createKDTree(ch2, depth + 1), splitAxis, splitPoint, new BoundingBox(objectList));

	}

	private static int getObjectPosition(ThreeDObject obj, double intersectPoint, int intersectAxis) {

		int erg = 0;

		switch (intersectAxis) {
		case 0:
			if (obj.getMaxX() <= intersectPoint) {
				erg = 2;
			} else if (obj.getMinX() >= intersectPoint) {
				erg = 1;
			} else {
				erg = 0;
			}

			break;

		case 1:
			if (obj.getMaxY() <= intersectPoint) {
				erg = 2;
			} else if (obj.getMinY() >= intersectPoint) {
				erg = 1;
			} else {
				erg = 0;
			}

			break;

		case 2:
			if (obj.getMaxZ() <= intersectPoint) {
				erg = 2;
			} else if (obj.getMinZ() >= intersectPoint) {
				erg = 1;
			} else {
				erg = 0;
			}

			break;

		default:
			erg = 0;
			break;
		}

		return erg;
	}

	private static double spaceMean(ArrayList<ThreeDObject> objectList, int intersectAxis) {

		BoundingBox bb = new BoundingBox(objectList);
		double erg;

		switch (intersectAxis) {
		case 0:
			erg = (bb.getMaxX() + bb.getMinX()) / 2;
			break;

		case 1:
			erg = (bb.getMaxY() + bb.getMinY()) / 2;
			break;

		case 2:
			erg = (bb.getMaxZ() / +bb.getMinZ()) / 2;
			break;

		default:
			erg = 0;
			break;
		}

		return erg;
	}

}
